#include "ObjImporter.h"

ObjImporter::ObjImporter()
{

}
ObjImporter::~ObjImporter()
{

}
bool ObjImporter::ImportObj( const std::string& file_name )
{
	// open file
	std::string fileName;

	//if( !FileServer::LocateResource("models/" + file_name, fileName) )
	if( !FileServer::LocateResource( file_name, fileName) )
	{
		std::cerr << "(ObjImporter) ERROR: cannot locate file '" << file_name << "'\n";
		return false;
	}

	std::ifstream ifs;
	ifs.open(fileName.c_str());

	if (! ifs)
	{
		std::cerr << "(ObjImporter) ERROR: cannot open file '" << file_name << "'\n";
		return false;
	}

	std::cout << "(ObjImporter) Loading " << file_name << "\n";
	// buffers to hold lines from the file
	std::string buffer, templine;

	// skip first 2 lines (comment)
	getline(ifs, buffer);
	getline(ifs, buffer);

	// material lib name
	getline(ifs, buffer);

	std::stringstream matlibline(buffer);
	std::string matlibtag, matlibname;

	matlibline >> matlibtag >> matlibname;

	//TObjMatValueVector materials;

	if (! (matlibtag == "mtllib"))
	{
		std::cerr << "(ObjImporter) WARNING: no material library name given.\n";
	}
	else
	{
		if (! SetupMaterials(matlibname, m_Mats))
		{
			std::cerr << "(ObjImporter) WARNING: could not setup materials, "
					  << "will set default material matWhite which has to be defined manually.'\n";
		}
	}

	// skip object name
	getline(ifs, buffer);

	// we need to store vertices, texture coords, vertex normals,
	// and material (texture) names
	//std::vector<Vector3f> pos, tex, norm;
	//TMatNameFaceIndexListMap matFaceListMap;

	float x,y,z;
	std::string mat, f1, f2, f3;
	VTNIndex idx1, idx2, idx3;
	FaceIndex tempFace;

	unsigned int totalFaceCount = 0;

	while(! ifs.eof())
	{
		getline(ifs, templine);

		std::stringstream str(templine);

		str >> buffer;

		if (buffer == "v") // vertex pos
		{
			str >> x >> y >> z;
			m_PosVec.push_back(Vector3f(x, y, z));
		}
		else if (buffer == "vt") // vertex texture coords
		{
			str >> x >> y;
			// ...
			//tex.push_back(Vector3f(0.5 + (0.5 - (x - 0.25)), 0.75 - (0.5 - y), 0.0));
			m_TexVec.push_back(Vector3f(1.0 - (1.0 - x), 1.0 - y, 0.0));
		}
		else if (buffer == "vn") // vertex normal
		{
			str >> x >> y >> z;
			m_NormVec.push_back(Vector3f(x, y, z));
		}
		else if (buffer == "usemtl") // material
		{
			if (m_Mats.size() != 0)
			{
				str >> mat;
			}
			else
			{
				mat = "matWhite";
			}
		}
		else if (buffer == "f") // face
		{
			str >> f1 >> f2 >> f3;

			// we need to have normals
			if (m_NormVec.size() == 0)
			{
				std::cerr << "(ObjImporter) ERROR: normals are missing in the OBJ file, exiting.\n";
				return false;
			}

			// three indeces for each face:
			// vertex pos / texture coord / normal

			std::string del("/");
			std::vector<std::string> exploded;
			ExplodeString(f1, exploded, del);

			assert(exploded.size() == 3);

			idx1.v  = atoi(exploded[0].c_str());
			idx1.vt = atoi(exploded[1].c_str());
			idx1.vn = atoi(exploded[2].c_str());

			exploded.clear();
			ExplodeString(f2, exploded, del);

			assert(exploded.size() == 3);

			idx2.v  = atoi(exploded[0].c_str());
			idx2.vt = atoi(exploded[1].c_str());
			idx2.vn = atoi(exploded[2].c_str());

			exploded.clear();
			ExplodeString(f3, exploded, del);

			assert(exploded.size() == 3);

			idx3.v  = atoi(exploded[0].c_str());
			idx3.vt = atoi(exploded[1].c_str());
			idx3.vn = atoi(exploded[2].c_str());

			exploded.clear();

			tempFace.vertex[0] = idx1;
			tempFace.vertex[1] = idx2;
			tempFace.vertex[2] = idx3;

			if (mat.size() != 0)
			{
				m_MatNameFaceIndexListMap[mat].push_back(tempFace);
				m_Faces.push_back( tempFace );

				++totalFaceCount;
			}
			else
			{
				std::cerr << "(ObjImporter) WARNING: no material defined for use with "
						  << "this face index, so I'll ignore it and go on.\n";
			}
		}
	}

	// close the file
	ifs.close();

	//std::cout<< "(ObjImporter) Done importing .obj file " << file_name << "\n";

	return true;
}
void ObjImporter::Test()
{
 	std::cout<< "Pos\n";
 	for( TVector3fVector::iterator vit = m_PosVec.begin(); vit != m_PosVec.end(); vit++ )
 	{
 		std::cout<< (*vit).x() << " " << (*vit).y() << " " << (*vit).z() << "\n";
 	}
 
 	std::cout<< "Tex\n";
 	for( TVector3fVector::iterator vit = m_TexVec.begin(); vit != m_TexVec.end(); vit++ )
 	{
 		std::cout<< (*vit).x() << " " << (*vit).y() << " " << (*vit).z() << "\n";
 	}
 
 	std::cout<< "Norm\n";
 	for( TVector3fVector::iterator vit = m_NormVec.begin(); vit != m_NormVec.end(); vit++ )
 	{
 		std::cout<< (*vit).x() << " " << (*vit).y() << " " << (*vit).z() << "\n";
 	}
}
Vector3f ObjImporter::Pos( const FaceIndex& face, unsigned int ver_idx ) const
{
	//assert( ver_idx > 0 &&  ver_idx < 3 );

	VTNIndex idx = face.vertex[ver_idx];

	//assert( idx.v > 0 && idx.v <= m_PosVec.size() );

	return m_PosVec[idx.v - 1];
}
bool ObjImporter::Norm( const FaceIndex& face, unsigned int ver_idx, Vector3f& n ) const
{
	//assert( ver_idx > 0 &&  ver_idx < 3 );

	VTNIndex idx = face.vertex[ver_idx];

	//assert( idx.vn > 0 && idx.vn <= m_NormVec.size() );

	if( idx.vn > 0 && idx.vn <= m_NormVec.size() )
	{
		n = m_NormVec[idx.vn - 1];
		return true;
	}
	else
	{
		return false;
	}
}
bool ObjImporter::TexCoor( const FaceIndex& face, unsigned int ver_idx, Vector3f& t ) const
{
	//assert( ver_idx > 0 &&  ver_idx < 3 );

	VTNIndex idx = face.vertex[ver_idx];

	//assert( idx.vt > 0 && idx.vt <= m_TexVec.size() );

	if( idx.vt > 0 && idx.vt <= m_TexVec.size() )
	{
		t = m_TexVec[idx.vt - 1];
		return true;
	}
	else
	{
		return false;
	}
}
bool ObjImporter::FindMat( const std::string& mat_name, ObjMatValues& new_mat )
{
	for( TObjMatValueVector::const_iterator vit = m_Mats.begin();
		vit != m_Mats.end(); vit ++ )
	{
		ObjMatValues mat = *vit;
		if( mat.name == mat_name )
		{
			new_mat = mat;
			return true;
		}
	}

	return false;
}
const TFaceIndexList& ObjImporter::MatFaceIndexList( const std::string &mat_name )
{
	return m_MatNameFaceIndexListMap[ mat_name ];
}
void ObjImporter::ExplodeString(std::string & input, std::vector<std::string> & output, std::string & del)
{
	size_t pos1 = 0;
	size_t pos2 = 0;

	while ((pos2 = input.find(del, pos1)) != std::string::npos)
	{
		output.push_back(input.substr(pos1, pos2 - pos1));
		pos1 = pos2 + 1;
		//pos2 = input.find(del, pos1);
	}

	output.push_back(input.substr(pos1, input.length() - pos1));
}
bool ObjImporter::SetupMaterials(std::string & matLibName, TObjMatValueVector & matVector)
{
	// open file
	std::string fileName;

	if( !FileServer::LocateResource("materials/" + matLibName, fileName) )
 	{
 		std::cerr << "(ObjImporter) WARNING: cannot locate file '" << matLibName << "'\n";
 		return false;
 	}

	std::ifstream ifs;
	ifs.open(fileName.c_str());

	if (! ifs)
	{
		std::cerr << "(ObjImporter) WARNING: cannot open file '" << fileName << "'\n";
		return false;
	}

	// buffers to hold lines from the file
	std::string buffer, templine;

	// skip first 2 lines (comment)
	getline(ifs, buffer);
	getline(ifs, buffer);

	std::string matName, texName;
	float x1,x2,x3;
	ObjMatValues* currentMat = 0;

	while(! ifs.eof())
	{
		buffer.clear();
		templine.clear();
		getline(ifs, templine);

		std::stringstream str(templine);

		str >> buffer;

		if (buffer == "newmtl")
		{
			// whenever we find the "newmtl" keyword, we create a
			// new struct and store it in the vector; we fill in
			// the values as we read the following lines
			str >> matName;
			ObjMatValues temp;
			temp.alpha = 0.0;
			temp.diffuseTexture.clear();
			temp.name.clear();
			temp.shininess = 0.0;
			temp.name = matName;
			matVector.push_back(temp);
			currentMat = &(matVector.back());
		}

		if (currentMat == 0)
		{
			continue;
		}

		if (buffer == "Ka")
		{
			str >> x1 >> x2 >> x3;
			currentMat->ambient[0] = x1;
			currentMat->ambient[1] = x2;
			currentMat->ambient[2] = x3;
		}
		else if (buffer == "Kd")
		{
			str >> x1 >> x2 >> x3;
			currentMat->diffuse[0] = x1;
			currentMat->diffuse[1] = x2;
			currentMat->diffuse[2] = x3;
		}
		else if (buffer == "Ks")
		{
			str >> x1 >> x2 >> x3;
			currentMat->specular[0] = x1;
			currentMat->specular[1] = x2;
			currentMat->specular[2] = x3;
		}
		else if (buffer == "Ns")
		{
			str >> x1;
			currentMat->shininess = x1;
		}
		else if (buffer == "d")
		{
			str >> x1;
			currentMat->alpha = x1;
		}

		else if (buffer == "map_Kd")
		{
			str >> texName;

 			if (! FileServer::LocateResource("textures/" + texName, currentMat->diffuseTexture))
 			{
 				std::cerr << "(ObjImporter) ERROR: cannot locate texture file '"
 						  <<  "textures/" << texName << "'\n";
 			}
		}
	}

// 	std::cout << "(ObjImporter) read " << matVector.size()
// 			  << " material definitions.\n";

	return true;
}


